package com.xyz.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

public abstract class BasicShiroFilter extends AuthenticatingFilter {

     private List<String> loginUrls;

     private List<String> ajaxSuffixs;

     public void setLoginUrls(List<String> loginUrls){
          this.loginUrls = loginUrls;
     }

     public void setAjaxSuffix(List<String> ajaxSuffixs){
          this.ajaxSuffixs = ajaxSuffixs;
     }

     public static final String LOGIN_FAIL_EXCEPTION = "loginFailException";

     @Override
     protected boolean isLoginRequest(ServletRequest request, ServletResponse response) {
          boolean flg = false;
          if(loginUrls != null && loginUrls.size() > 0){
               for(String pattern : loginUrls){
                    if(pathsMatch(pattern,request)){
                         flg = true;
                         break;
                    }
               }
          }
          return flg;
     }

     @Override
     public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
          return this.isLoginRequest(request, response) ? false : super.isAccessAllowed(request, response, mappedValue);
     }

     @Override
     public boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
          if (isLoginRequest(request, response)) {
               if (this.isLoginSubmission(request, response)) {
                  return this.executeLogin(request, response);
               } else {
                    return true;
               }
          } else {
               this.saveRequestAndRedirectToLogin(request, response);
               return false;
          }
     }

     protected boolean isLoginSubmission(ServletRequest request, ServletResponse response) {
          return request instanceof HttpServletRequest && WebUtils.toHttp(request).getMethod().equalsIgnoreCase("POST");
     }

     @Override
     public void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
          if(isAjaxRequest(request)){
               response401(request, response);
               return;
          }
          super.saveRequestAndRedirectToLogin(request, response);
     }

     @Override
     protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
          this.loginSuccess(token,subject,request,response);
          request.setAttribute(LOGIN_FAIL_EXCEPTION, "");
          return this.isAjaxRequest(request) ? true : super.onLoginSuccess(token, subject, request, response);
     }

     public abstract void loginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response);

     public boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
          this.loginFailure(token, e, request, response);
          String className = e.getClass().getName();
          request.setAttribute(LOGIN_FAIL_EXCEPTION, className);
          return true;
     }

     public abstract void loginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response);

     /**
      * 对跨域提供支持
      */
     @Override
     protected void postHandle(ServletRequest request, ServletResponse response) throws Exception {
          fillCorsHeader(WebUtils.toHttp(request),WebUtils.toHttp(response));
     }

     protected void response401(ServletRequest request, ServletResponse response) throws IOException {
          if(isAjaxRequest(request)){
               HttpServletResponse httpResponse = WebUtils.toHttp(response);
               httpResponse.setCharacterEncoding("UTF-8");
               httpResponse.setContentType("application/json;charset=UTF-8");
               // 没有授权信息
               httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
          }else{
               WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
          }
     }

     /**
      * 支持跨域访问
      * @param httpServletRequest
      * @param httpServletResponse
      */
     protected void fillCorsHeader(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){
          httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
          httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,HEAD");
          httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
     }

     /**
      * 获得Header信息
      * @param request
      * @param headerName
      * @return
      */
     protected String getHeaderInfo(HttpServletRequest request, String headerName){
          return request.getHeader(headerName);
     }

     /**
      * 设置Header信息
      * @param response
      * @param headerName
      * @param headerValue
      */
     protected void setHeader(HttpServletResponse response, String headerName, String headerValue){
          response.setHeader(headerName, headerValue);
     }

     /**
      * 是否为Ajax请求
      * @param servletRequest
      * @return
      */
     protected boolean isAjaxRequest(ServletRequest servletRequest){
          HttpServletRequest request = WebUtils.toHttp(servletRequest);
          String requestType = WebUtils.toHttp(request).getHeader("X-Requested-With");
          // 是 ajax 请求的场合，当作 rest api 调用处理
          // 不是 jhtml 请求的场合，当作 rest api 调用处理
          return "XMLHttpRequest".equals(requestType) ||isEndWithAjaxSuffix(request);
     }


     /**
      * 是否以设定的后缀结尾的请求
      * @param request
      * @return
      */
     private boolean isEndWithAjaxSuffix(HttpServletRequest request){
          if(this.ajaxSuffixs == null || this.ajaxSuffixs.size() == 0){
               return false;
          }
          if(this.ajaxSuffixs.size() == 1){
               return request.getRequestURI().toLowerCase().endsWith(this.ajaxSuffixs.get(0));
          }
         return this.ajaxSuffixs.stream().filter(x->{
              return request.getRequestURI().toLowerCase().endsWith(x);
          }).collect(Collectors.toList()).size() > 0;
     }



}
